
#include<iostream>
#include<thread>
#include<vector>
#include <list>

//引入互斥量类
#include<mutex>
using namespace std;

//保护共享数据
//操作时用代码把共享数据锁住，其他操作必须等待解锁
// 按顺序读取，锁定，操作，解锁，下一轮操作
//互斥量的基本概念：
//一个类对象，可以理解为一把锁
// 多个线程尝试用lock方法为线程加锁，只有一个线程可以被锁成功
// 如果没有锁成功，则会一直停留在locak（）环节
// 
// 互斥量使用要小心，保护数据不多不少，多了会影响效率，少了起不到保护的效果
// 
// 用法：
// lock（）  unlock
// 先用lock操作共享数据
//lock和unlock要成对使用，不允许多次调用
//为了防止忘记unlock（），引入了一个std：：lock_guard的类模板
//可以自动unlock（）
// 
// 
//使用lock_guard后就不能使用lock和unlock，只有使用一个lockguard即可
//他的工作原理就是在lockguard类中的构造和析构里分别放了一个lock和unlock
//如果lockguar创建在堆区，可手动释放内存，也大多同样的效果
//因为只有一个步骤，所以不太灵活
// 可以通过手动加作用域{}提前释放
// 
// 
// 死锁：
// 张三在北京喊李四，不移动
// 李四在上海喊张三，不移动
// 他们最终谁都找不到谁
// 
// 一把金锁，一把银锁，进程a执行，锁住了金锁，希望锁住银锁，
// 此时进程b执行，他的银锁被锁定，希望锁金锁，此时产生了两个线程分别希望锁
// 金锁/银锁 ，但是都无法实现
// 
// 代码实现：必须至少有两个锁
// 并且两个线程的锁定顺序不同
// 
// 解决：
// 1.保证两个线程的上锁顺序一致，就不会死锁
// 2.使用全局函数std：：lock（）
// 此时传入参数的顺序不影响，他的工作原理就是判断释放传入的参数互斥量释放同时
// 都被锁住，如果是，则正常运行，否则全部解锁，以保证互斥量的一致性.
// 最后也要分别unlock
// 为了防止忘记unlock，可以使用lockguard成员函数，
// 把每一个互斥量的lockguard函数最后传入一个参数std：：adopt_lock来禁止锁定
// （因为在lock（）中已经被锁定了，此时lockguard函数只做解锁的功能
// 
//但是建议分开lock（） 
//


class A {
public:
	//线程入口，读入消息队列

	void inMsgRecvQueue() {
		for (int i = 0; i < 100000; ++i) {
			cout << "插入元素" << i << endl;

			lock(my_mutex, my_mutex2);
			lock_guard<mutex> my_guard(my_mutex, adopt_lock);
			lock_guard<mutex> my_guard2(my_mutex2, adopt_lock);
			msgRecvQueue.push_back(i);
		}
	}

	bool outMsgLULProc(int& command) {


		lock(my_mutex, my_mutex2);
		lock_guard<mutex> my_guard(my_mutex, adopt_lock);
		lock_guard<mutex> my_guard2(my_mutex2, adopt_lock);

		if (!msgRecvQueue.empty()) {
			//消息不为空
			int command = msgRecvQueue.front();
			msgRecvQueue.pop_front();
			return true;
		}
		else {
			return false;
		}
		
	}




	//线程入口，取出消息队列
	void outMsgRecvQueue() {

		int commant = 0;
		for (int i = 0; i < 100000; ++i) {
			if (outMsgLULProc(commant)) {
				//具体操作

			}
			else {
				//如果为空
				cout << "消息为空" << i << endl;
			}
		}
		cout << "消息处理结束" << endl;
	}

private:
	//用来存放数据的容器
	//共享数据
	list<int> msgRecvQueue;
	//引入互斥量类
	mutex my_mutex;
	mutex my_mutex2;
};


int main(int argc, char* argv[]) {

	//创建线程,第二个参数用引入方式传递，保证一直都是源对象
	//第二个参数表示运行的目标，即第一个参数作为线程在第二个参数对象上运行
	A myobja;
	thread myOutMsgObj(&A::outMsgRecvQueue, &myobja);
	thread myInMsgObj(&A::inMsgRecvQueue, &myobja);
	myOutMsgObj.join();
	myInMsgObj.join();
	//上述代码会因为读写问题报错
	//引入互斥量，来解决问题



	//最后主线程执行，进程退出
	cout << "Hello" << endl;
	return 0;
}